/* 
  Copyright (c) SilverLining 2015
  Date:    2016-1-7
  File:    timer_state.cpp  
  Description: 

*/

#include "stdafx.h"


using namespace m::alloc;
using namespace m::error;

namespace m
{
	timer_state::timer_state()
		: _PmWeak(nullptr),
		_Tmr(NULL),
		_Sender(nullptr),
		_Evt(nullptr),
		_Data(nullptr)
	{

	}

	timer_state::~timer_state()
	{
		clear();
	}

	void* timer_state::operator new(size_t sz)
	{
		return ::operator new(sz, pool);
	}

	void timer_state::operator delete(void* ptr)
	{
		::operator delete(ptr, pool);
	}

	void timer_state::CreateOrSet(process_manager* Pm, const scalar& delay, const scalar& interval, const bool& oneShot, process* sender, const wchar_t* evt, object* data)
	{
		ref_storage track1;
		weak_ref* pmWeak = create_weak(track1, Pm);
		assign(_PmWeak, pmWeak);
		assign(_Evt, evt);
		ref_storage track2;
		weak_ref* senderWeak = create_weak(track2, sender);
		assign(_Sender, senderWeak);
		assign(_Data, data);

		DWORD flags = WT_EXECUTEINTIMERTHREAD;
		if (oneShot)
		{
			if (fabs(interval) >= SCALAR_EPSILON)
			{
				throw bad_result_exception(MONORET(FACILITY_PROCESS, ERROR_BAD_ARGUMENTS), "Failed to start timer. The interval is invalid.");
			}
			flags |= WT_EXECUTEONLYONCE;
		}

		assert(Pm);
		if (Pm)
		{
			if (!CreateTimerQueueTimer(
				&_Tmr,
				Pm->_TimerQ,
				timer_stateCallback,
				this,
				(DWORD)(delay * 1000.),
				oneShot ? 0 : (DWORD)(interval * 1000.),
				flags
				))
			{
				throw bad_result_exception(HRESULT_FROM_WIN32(GetLastError()), "Failed to set timer.");
			}
		}
	}

	void timer_state::clear()
	{
		ref_storage track1;
		process_manager* pm = lock<process_manager>(track1, _PmWeak);

		if (_Tmr)
		{
			if (pm)
			{
				if (!DeleteTimerQueueTimer(pm->_TimerQ, _Tmr, INVALID_HANDLE_VALUE))
				{
					if (GetLastError() != ERROR_IO_PENDING)
					{
						throw bad_result_exception(HRESULT_FROM_WIN32(GetLastError()), "Failed to clear or reset timer.");
					}
				}
			}
			_Tmr = NULL;
		}

		m::clear(_PmWeak);
		m::clear(_Sender);
		m::clear(_Data);
		m::clear(_Evt);
	}

	VOID CALLBACK timer_stateCallback(
		_In_ PVOID   lpParameter,
		_In_ BOOLEAN TimerOrWaitFired
		)
	{
		assert(lpParameter);
		if (lpParameter)
		{
			ref_storage track1, track2, track3;
			timer_state* ts = m::reference(track1, static_cast<timer_state*>(lpParameter));
			process_manager* pm = lock<process_manager>(track2, ts->_PmWeak);

			if (pm)
			{
				process* sender = lock<process>(track3, ts->_Sender);
				pm->send(sender, ts->_Evt, ts->_Data, true);
			}
		}
	}
}